package com.changgou.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.seckill.config.ConfirmMessageSender;
import com.changgou.seckill.config.RabbitMQConfig;
import com.changgou.seckill.dao.SeckillGoodsMapper;
import com.changgou.seckill.dao.SeckillOrderMapper;
import com.changgou.seckill.pojo.SeckillGoods;
import com.changgou.seckill.pojo.SeckillOrder;
import com.changgou.seckill.service.SecKillOrderService;
import com.changgou.util.IdWorker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @Author LQQ
 * @Date 2020/3/18 22:38
 * @Version 1.0
 */
@Service
public class SecKillOrderServiceImpl implements SecKillOrderService {
    @Autowired
    private SeckillOrderMapper seckillOrderMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IdWorker idWorker;
    //自定义消息发送类
    @Autowired
    private ConfirmMessageSender confirmMessageSender;

    public static final String SECKILL_GOODS_KEY = "seckill_goods_";

    public static final String SECKILL_GOODS_STOCK_COUNT_KEY = "seckill_goods_stock_count_";

    /**
     * 秒杀下单
     *
     * @param id
     * @param time
     * @param username
     * @return
     */
    public boolean add(Long id, String time, String username) {
        /**
         * 1.获取redis中的商品信息与库存信息,并进行判断
         * 2.执行redis的预扣减库存操作,并获取扣减之后的库存值
         * 3.如果扣减之后的库存值<=0,则删除redis中响应的商品信息与库存信息
         * 4.基于mq完成mysql的数据同步,进行异步下单并扣减库存(mysql)
         */

        //防止用户恶意刷单
        String result = this.preventRepeatCommit(username, id);

        if ("fail".equals(result)) {
            //这里若return false,下面方法都不执行
            return false;
        }

        //防止相同商品重复购买
        SeckillOrder orderInfoByUserNameAndGoodsId = seckillOrderMapper.getOrderInfoByUserNameAndGoodsId(username,id);
        if (orderInfoByUserNameAndGoodsId != null) {
            return false;

        }

        //1.获取商品信息
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(SECKILL_GOODS_KEY + time).get(id);

        //2.获取库存信息
        String redisStock = (String) redisTemplate.opsForValue().get(SECKILL_GOODS_STOCK_COUNT_KEY + id);

        if (StringUtils.isEmpty(redisStock)) {

            return false;
        }

        //2.1 转换为数值型
        int stock = Integer.parseInt(redisStock);

        //3.判断
        if (seckillGoods == null || stock <= 0) {
            return false;
        }

        //4.执行redis预扣减库存,并获取到扣减之后的库存值.decrement=扣减后的库存
        //decrement减,increment加
        Long decrement = redisTemplate.opsForValue().decrement(SECKILL_GOODS_STOCK_COUNT_KEY + id);

        if (decrement <= 0) {
            //4.1扣减库存后,当前商品已经没有库存,删除redis中的商品信息与库存
            redisTemplate.boundHashOps(SECKILL_GOODS_KEY + time).delete(id);//删除商品信息

            redisTemplate.delete(SECKILL_GOODS_STOCK_COUNT_KEY + id);//库存信息

        }

        //5.发送消息 保证消息生产者对于消息的不丢失实现
        //发送的消息体是订单对象
        SeckillOrder seckillOrder = new SeckillOrder();

        seckillOrder.setId(idWorker.nextId());//订单id
        seckillOrder.setSeckillId(id);//商品id
        seckillOrder.setMoney(seckillGoods.getCostPrice());//订单价钱
        seckillOrder.setUserId(username);//用户
        seckillOrder.setSellerId(seckillGoods.getSellerId());//商家id
        seckillOrder.setCreateTime(new Date());//创建时间
        seckillOrder.setStatus("0");

        //发送消息
        confirmMessageSender.sendMessage("", RabbitMQConfig.SECKILL_ORDER_QUEUE, JSON.toJSONString(seckillOrder));

        return true;
    }

    //定义一个私有的方法,防止重复提交
    private String preventRepeatCommit(String username, Long id) {
        String redis_key = "seckill_user_" + username + "_id_" + id;

        //+1
        long count = redisTemplate.opsForValue().increment(redis_key, 1);

        if (count == 1) {
            // 代表当前用户是第一次访问
            //对当前的key设置一个五分钟的有效期
            redisTemplate.expire(redis_key, 5, TimeUnit.MINUTES);

            return "success";

        }
        if (count > 1) {
            return "fail";
        }
        return "fail";
    }

}
